﻿namespace WLFramework.Mapping
{
    using System;
    using System.Collections.Generic;
    using System.Reflection;
    using WLFramework.Reflection;
    using WLFramework.Validation;

    internal class InternalMemberInfo : IEqualityComparer<InternalMemberInfo>
    {
        internal enum MemberTypes 
        {
            Property = 1,
            Field = 2,
        }

        private PropertyInfo _Property;
        private FieldInfo _Field;
        private MemberTypes _MemberType;

        public string MemberName { get; set; }

        public Type ValueType 
        {
            get
            {
                return _MemberType == MemberTypes.Property ?
                    _Property.PropertyType :
                    _Field.FieldType;
            }
        }

        public TargetGetter Getter()
        {
            switch (_MemberType)
            {
                case MemberTypes.Field:
                    return _Field.CreateFieldGetter();

                case MemberTypes.Property:
                    return _Property.CreatePropertyGetter();

                default:
                    throw new ArgumentException("MemberType");
            }
        }

        public TargetSetter Setter()
        {
            switch (_MemberType)
            {
                case MemberTypes.Field:
                    return _Field.CreateFieldSetter();

                case MemberTypes.Property:
                    return _Property.CreatePropertySetter();

                default:
                    throw new ArgumentException("MemberType");
            }
        }

        private InternalMemberInfo()
        { 
        }

        public static List<InternalMemberInfo> GetMemberInfo(Type type)
        {
            List<InternalMemberInfo> result = new List<InternalMemberInfo>();

            foreach (var prop in type.GetProperties())
            {
                if (!(prop.CanRead && prop.CanWrite))
                    continue;

                var ignore = type.GetAttribute<IgnoreAttribute>(true);
                if (ignore != null && ignore.IsIgnoreMapping)
                {
                    continue;
                }

                if (type.IsClass)
                {
                    if (type.GetConstructor(Type.EmptyTypes) == null)
                        continue;
                }

                var member = prop.GetAttribute<MappingMemberAttribute>(true);
                
                result.Add(new InternalMemberInfo()
                {
                    MemberName = member != null ? member.MemberName : prop.Name,
                    _MemberType = MemberTypes.Property,
                    _Property = prop
                });
            }

            foreach (var field in type.GetFields())
            {
                var ignore = type.GetAttribute<IgnoreAttribute>(true);
                if (ignore != null && ignore.IsIgnoreMapping)
                {
                    continue;
                }

                if (type.IsClass)
                {
                    if (type.GetConstructor(Type.EmptyTypes) == null)
                        continue;
                }

                var member = field.GetAttribute<MappingMemberAttribute>(true);

                result.Add(new InternalMemberInfo()
                {
                    MemberName = member != null ? member.MemberName : field.Name,
                    _MemberType = MemberTypes.Field,
                    _Field = field
                });
            }

            return result;
        }

        public bool Equals(InternalMemberInfo x, InternalMemberInfo y)
        {
            return x.MemberName == y.MemberName;
        }

        public int GetHashCode(InternalMemberInfo obj)
        {
            return obj.MemberName.GetHashCode();
        }
    }
}
